package com.bridle.james.austeer; import android.Manifest; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.pm.PackageManager; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.os.Environment; import android.support.v4.app.ActivityCompat; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.os.Handler; import android.util.Log; import android.view.WindowManager; import android.widget.TextView; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.math.RoundingMode; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; public class Logging extends AppCompatActivity { TextView timerTextView; long startTime = 0; private static final String TAG = "AusteerLogging"; private String timeStamp = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(System.currentTimeMillis()); private String filename = "Austeer-" + timeStamp + ".csv"; private String filepath = "Austeer"; File envpath; File outputFile; String outputString = ""; FileWriter fw; TextView speedTextView; TextView locationTextView; TextView steeringTextView; TextView storageTextView; String locString = ""; String altitudeString = ""; String speedString = ""; String steerStringX; String steerStringY; String steerStringZ; List<Double> speedlist = new ArrayList(); Double lastLat; Double lastLng; Double lastAlt; Long lastTime; LocationManager locationManager; LocationListener li; SensorManager sMgr; Sensor gyro; SensorEventListener sev; //runs without a timer by reposting this handler at the end of the runnable Handler timerHandler = new Handler(); Runnable timerRunnable = new Runnable() { @Override public void run() { long millis = System.currentTimeMillis() - startTime; int seconds = (int) (millis / 1000); int minutes = seconds / 60; seconds = seconds % 60; timerTextView.setText(String.format("%d:%02d", minutes, seconds)); locationTextView.setText(locString); speedTextView.setText(speedString); steeringTextView.setText(steerStringY); outputString = String.format("%d:%02d:%02d", minutes, seconds, millis % 1000) + "," + locString + "," + speedString + "," + steerStringY + "," + altitudeString + "\n"; try { fw.write(outputString); storageTextView.setText("Writing to " + filename); } catch (IOException e) { storageTextView.setText(getString(R.string.storage_problem)); } timerHandler.postDelayed(this, 500); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_logging); storageTextView = (TextView) findViewById(R.id.storageTextView); String state = Environment.getExternalStorageState(); if (Environment.MEDIA_MOUNTED.equals(state)) { storageTextView.setText(getString(R.string.storage_writable)); envpath = Environment.getExternalStoragePublicDirectory(filepath); outputFile = new File(envpath, filename); envpath.mkdirs(); try { fw = new FileWriter(outputFile); } catch (IOException e) { storageTextView.setText(getString(R.string.storage_problem)); } } else { storageTextView.setText(getString(R.string.storage_not_writable)); } final DecimalFormat df = new DecimalFormat("#.####"); df.setRoundingMode(RoundingMode.CEILING); final DecimalFormat df2 = new DecimalFormat("#.#"); df2.setRoundingMode(RoundingMode.CEILING); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); timerTextView = (TextView) findViewById(R.id.timerTextView); locationTextView = (TextView) findViewById(R.id.locationTextView); speedTextView = (TextView) findViewById(R.id.speedTextView); steeringTextView = (TextView) findViewById(R.id.steeringTextView); locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE); li = new LocationListener() { public void onLocationChanged(Location location) { // Called when a new location is found by the network location provider. Double newLat = location.getLatitude(); Double newLng = location.getLongitude(); Double newAlt = location.getAltitude(); Long newTime = location.getTime(); altitudeString = Double.toString(newAlt); Float accuracy = location.getAccuracy(); // Altitude too inaccurate so just use the same altitude for calculating speed, // you're not moving that fast unless you fall off a cliff locString = df.format(newLat) + ", " + df.format(newLng); Log.v(TAG, "LOCATION CHANGE, lat=" + df.format(newLat) + ", lon=" + df.format(newLng) + "(Accuracy: " + accuracy + ")"); // on first run set location and start timer if (lastLat == null) { startTime = System.currentTimeMillis(); timerHandler.postDelayed(timerRunnable, 0); speedString = "0"; lastLat = newLat; lastLng = newLng; lastAlt = newAlt; lastTime = newTime; } else { Double dist = distance(lastLat, newLat, lastLng, newLng, newAlt, newAlt); Log.v(TAG, "DISTANCE = " + dist); float timediff = (newTime - lastTime) / 1000; if (timediff == 0) { // do nothing because distance is less than accuracy // or measurement too quick Log.v(TAG, "SPEED UNCHANGED"); } else { Double speed = Math.abs(dist) / timediff; // Average speed from last five position results speedlist.add(speed); if (speedlist.size() > 5) { speedlist.remove(0); Double averagespeed = averageSpeed(speedlist); if (averagespeed < 0.5) { averagespeed = 0.0; } speedString = df2.format(Math.abs(averagespeed)); Log.v(TAG, "SPEEDS = "+speedlist.toString()); Log.v(TAG, "ALTITUDE = "+altitudeString); } // only update speeds if dist/time is changed lastLat = newLat; lastLng = newLng; lastAlt = newAlt; lastTime = newTime; } } } public void onStatusChanged(String provider, int status, Bundle extras) { } public void onProviderEnabled(String provider) { } public void onProviderDisabled(String provider) { } }; if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { new AlertDialog.Builder(this) .setTitle("Permissions Needed") .setMessage("You need to give Austeer permission to use your location.") .setNeutralButton(android.R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { // do nothing } }) .setIcon(android.R.drawable.ic_dialog_alert) .show(); } locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, li); locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, li); sMgr = (SensorManager) getSystemService(SENSOR_SERVICE); gyro = sMgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); // With Accelerometer, flat on a table is X-0, Y-0, Z-10 // Held flat upward in portrait mode, X-0, y-10, Z-0 // Held flat upward in landscape: X-10, Y-0, Z-0 // Steering mode fixed to steering wheel landscape: Just use Y-value sev = new SensorEventListener() { public void onAccuracyChanged(Sensor sensor, int accuracy) { } public void onSensorChanged(SensorEvent event) { float axisX = event.values[0]; float axisY = event.values[1]; float axisZ = event.values[2]; // Trying to remove negative zero but doing it wrong. // if (1 / axisY > 0) { } else { axisY = Math.abs(axisY); } steerStringX = Float.toString(Math.round(axisX * 10) / 10); steerStringY = String.format("%.2f", axisY / 10); steerStringZ = Float.toString(Math.round(axisZ * 10) / 10); } }; sMgr.registerListener(sev, gyro, SensorManager.SENSOR_DELAY_FASTEST); } @Override public void onPause() { super.onPause(); sMgr.unregisterListener(sev); timerHandler.removeCallbacks(timerRunnable); try { fw.flush(); fw.close(); } catch (IOException e) { e.printStackTrace(); } } @Override public void onResume() { super.onResume(); } /** * Calculate distance between two points in latitude and longitude taking * into account height difference. If you are not interested in height * difference pass 0.0. Uses Haversine method as its base. * * lat1, lon1 Start point lat2, lon2 End point el1 Start altitude in meters * el2 End altitude in meters * @returns Distance in Meters */ public Double distance(double lat1, double lat2, double lon1, double lon2, double el1, double el2) { final int R = 6371; // Radius of the earth Double latDistance = Math.toRadians(lat2 - lat1); Double lonDistance = Math.toRadians(lon2 - lon1); Double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2) + Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) * Math.sin(lonDistance / 2) * Math.sin(lonDistance / 2); Double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); double distance = R * c * 1000; // convert to meters double height = el1 - el2; distance = Math.pow(distance, 2) + Math.pow(height, 2); return Math.sqrt(distance); } public Double averageSpeed(List speeds) { Double total = 0.0; for (int i = 0; i < speeds.size(); i++) { total = total + (double)speeds.get(i); } return (total / speeds.size()); } }